import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Observable;

import javax.swing.JOptionPane;

/**
 * This class is the model in the model-view controller pattern;
 * This class will contain the dimensions of the brick, with a length,
 * width and height. The model may or may not have a file associated
 * with it, where the model is stored
 * @author Sophia Soohoo
 *
 */
public class BrickModel extends Observable implements Serializable, 
    Originator {

    /***/
    private static final long serialVersionUID = 1L;
    
    /**the file where the BrickModel is stored*/
    private File file;
    
    /**has the model changed since last save*/
    private boolean changed = true;
    
    /**the width of the brick*/
    private double width;
    
    /**the height of the brick*/
    private double height;
    
    /**the length of the brick*/
    private double length;
    
    
    /**
     * Default constructor sets the brick with null for the file,
     * has been changed, and makes the block a 1x1x1 cube
     */
    public BrickModel() throws BrickException{
    	this(null, false, 1, 1, 1);
    }
    
    /**
     * Sets the current model as a model provided by the user
     * @param model
     */
    public BrickModel(BrickModel model) throws BrickException {
        this();
        setHeight(model.height);
        setWidth(model.width);
        setLength(model.length);
        setFile(model.file);
        setChanged(model.changed);
    }
    
    
	/**
	 * @param width    the width of the block
	 * @param height   the height of the block
	 * @param length   the length of the block
	 */
	public BrickModel(double width, double height, double length) 
	    throws BrickException {
		this(null, false, width, height, length);
	}
   
	
	/**
	 * @param changed  whether or not the model has been changed since last save
	 * @param width    the width of the block
	 * @param height   the height of the block
	 * @param length   the length of the block
	 */
	public BrickModel(boolean changed, double width, double height,
			double length) throws BrickException {
		this(null, changed, width, height, length);
	}

	
	/**
	 * @param file  the file where the model is stored
	 * @param changed  whether or not the model has been changed since last save
	 * @param width    the width of the block
	 * @param height   the height of the block
	 * @param length   the length of the block
	 * @throws BrickException 
	 * @exception  NumberFormatException  exception thrown if a value for 
	 *             length, width or height <= 0
	 */
	public BrickModel(File file, boolean changed, double width, double height,
			double length) throws BrickException {
		super();
		this.file = file;

		setWidth(width);
		setHeight(height);
		setLength(length);
		this.changed = changed;

	}


	/**
     * @return the file
     */
    public File getFile() {
        return file;
    }

    
    /**
     * @param file the file to set
     */
    public void setFile(File file) {
        this.file = file;
        setChanged(true);
    }

    
    /**
     * @return the changed
     */
    public boolean isChanged() {
        return changed;
    }

    
    /**
     * @param set the model as changed, and notify observers
     */
    public void setChanged(boolean changed) {


    	if (changed == true) {
    	    this.changed = true;
        	super.setChanged();
        	notifyObservers();    		
    	} else {
    	    this.changed = false;
    	}
    }
    
    /**
     * Saves the model to the current file
     */
    public void save() {
        try {
            ObjectOutputStream oos = new ObjectOutputStream 
                (new FileOutputStream(file));
            setChanged(false);
            oos.writeObject (this);
        } catch(IOException e) {
            JOptionPane.showMessageDialog(null, "Brick could not be saved");
        }
    }   

    
    /**
     * @return the width
     */
    public double getWidth() {
        return width;
    }

    
    /**
     * @param width the width to set
     * @exception NumberFormatException  thrown if width <= 0
     */
    public void setWidth(double width) throws BrickException {
        if (width <= 0) 
            throw new BrickException("Width must be greater than zero");
        
        this.width = width;
        setChanged(true);            
        
    }

    
    /**
     * @return the height
     */
    public double getHeight() {
        return height;
    }

    
    /**
     * @param  new height 
     * @throws BrickException 
     * @exception NumberFormatException exception thrown if height <= 0
     */
    public void setHeight(double height) throws BrickException {
        
        if (height <= 0) 
            throw new BrickException("Height must be greater than zero");
        this.height = height;
        setChanged(true);            
    }

    
    /**
     * @return the length
     */
    public double getLength() {
        return length;
    }

    
    /**
     * @param length the length to set
     * @throws BrickException 
     */
    public void setLength(double length) throws BrickException {
        
        if (length <= 0) 
            throw new BrickException("Length must be greater than zero");  
        this.length = length;
        setChanged(true);            
    }
    
	/**
	 * @author Sophia
     * @param model the model to set
     */
    public void setModel (BrickModel newModel) throws BrickException {
        setFile(newModel.getFile());
        setHeight(newModel.getHeight());
        setWidth(newModel.getWidth());
        setLength(newModel.getLength());
        setChanged(true);
        setChanged(newModel.isChanged());
    }

    
    /**
     * hash code
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (changed ? 1231 : 1237);
        result = prime * result + ((file == null) ? 0 : file.hashCode());
        long temp;
        temp = Double.doubleToLongBits(height);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(length);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(width);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    
    /**
     * equals
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        BrickModel other = (BrickModel) obj;
        if (changed != other.changed)
            return false;
        if (file == null) {
            if (other.file != null)
                return false;
        } else if (!file.equals(other.file))
            return false;
        if (Double.doubleToLongBits(height) != Double
                .doubleToLongBits(other.height))
            return false;
        if (Double.doubleToLongBits(length) != Double
                .doubleToLongBits(other.length))
            return false;
        if (Double.doubleToLongBits(width) != Double
                .doubleToLongBits(other.width))
            return false;
        return true;
    }

    
    /**
     * cloneable not supported
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    
    /**
     * to String
     */
    @Override
    public String toString() {
        
        return "Brick height = "+ height + 
            "\nBrick width = " +    width  +
            "\nBrick length = " +   length;
    }

    /**
     * make a memento 
     * @return a concrete memento
     */
    @Override
    public Memento makeMemento() {
        return (Memento)new BrickMemento(height, width, length);
    }

    /** 
     * use a memento to restore a previous state to the model
     * @param a memento to restore
     */
    @Override
    public void restore(Memento m) throws BrickException {
               
        if (m instanceof BrickMemento) {
        	BrickMemento memento = (BrickMemento)m;
            setWidth(memento.getWidth());
            setHeight(memento.getHeight());
            setLength(memento.getLength());
            setChanged(true);
        }
    }
    
}
